package com.gitee.wsl.common.ui.symbol


import androidx.compose.ui.graphics.Path
import com.gitee.wsl.common.ui.base.Point
import com.gitee.wsl.ext.list.setAll
import com.gitee.wsl.mathematics.geometry.shape.Line
import com.gitee.wsl.mathematics.geometry.shape.Rectangle
import kotlin.random.Random

open class BezierLine(startPoint: Point, endPoint: Point): Line(startPoint, endPoint){

    val bezierPath: Path = Path() // 贝塞尔曲线路径

    protected var bezierPointList = mutableListOf<Point>() // 贝塞尔曲线点集

    protected var bezierPoint: Point? = null // 贝塞尔曲线移动点

    protected val controlPointList = mutableListOf<Point>() // 控制点集

    // 切线点集
    protected var tangentPointList: ArrayList<ArrayList<ArrayList<Point>>> = ArrayList()

    protected var instantTangentPointList: ArrayList<ArrayList<Point>> = ArrayList()

    var mR = 0 // 移动速率

    var mRate = RATE // 速率

    var mState:Int  = STATE_READY or STATE_TOUCH // 状态

    var mLoop = false // 设置是否循环

    var mTangent = true // 设置是否显示切线

    var mWidth = 0
    var mHeight:Int = 0 // 画布宽高

    var mCurPoint: Point? = null // 当前移动的控制点

    fun addControlPoints(points: Point){
        controlPointList.add(points)
        buildBezierPoints()
    }

    fun addControlPoints(points: Array<Point>){
        controlPointList.addAll(points)
        buildBezierPoints()
    }

    fun setKnotsPoint(offsetList: List<Point>){
        controlPointList.setAll(calculateControlPoint(offsetList))
        buildBezierPoints()
    }

    /**
     * 创建Bezier点集
     *
     * @return
     */
    fun buildBezierPoints(): ArrayList<Point> {
        val points = ArrayList<Point>()
        val order = controlPointList.size - 1
        val delta = 1.0f / FRAME
        var t = 0f
        while (t <= 1) {
            // Bezier点集
            points.add(Point(deCasteljauX(order, 0, t), deCasteljauY(order, 0, t)))
            t += delta
        }
        return points
    }

    /**
     * 创建切线点集
     */
    fun buildTangentPoints(): ArrayList<ArrayList<ArrayList<Point>>> {
        var points: ArrayList<Point> // 1条线点集
        var morepoints: ArrayList<ArrayList<Point>> // 多条线点集
        val allpoints = ArrayList<ArrayList<ArrayList<Point>>>() // 所有点集
        var point: Point
        val order = controlPointList.size - 1
        val delta = 1.0f / FRAME
        for (i in 0 until order - 1) {
            val size = allpoints.size
            morepoints = ArrayList()
            for (j in 0 until order - i) {
                points = ArrayList()
                var t = 0f
                while (t <= 1) {
                    var p0x: Float
                    var p1x: Float
                    var p0y: Float
                    var p1y: Float
                    val z = (t * FRAME).toInt()
                    if (size > 0) {
                        p0x = allpoints[i - 1][j][z].x
                        p1x = allpoints[i - 1][j + 1][z].x
                        p0y = allpoints[i - 1][j][z].y
                        p1y = allpoints[i - 1][j + 1][z].y
                    } else {
                        p0x = controlPointList[j].x
                        p1x = controlPointList[j + 1].x
                        p0y = controlPointList[j].y
                        p1y = controlPointList[j + 1].y
                    }
                    val x = (1 - t) * p0x + t * p1x
                    val y = (1 - t) * p0y + t * p1y
                    point = Point(x, y)
                    points.add(point)
                    t += delta
                }
                morepoints.add(points)
            }
            allpoints.add(morepoints)
        }
        return allpoints
    }

    /**
     * deCasteljau算法
     *
     * @param i 阶数
     * @param j 点
     * @param t 时间
     * @return
     */
    private fun deCasteljauX(i: Int, j: Int, t: Float): Float {
        return if (i == 1) {
            (1 - t) * controlPointList[j].x + t * controlPointList[j + 1].x
        } else
            (1 - t) * deCasteljauX(i - 1, j, t) + t * deCasteljauX(i - 1, j + 1, t)
    }

    /**
     * deCasteljau算法
     *
     * @param i 阶数
     * @param j 点
     * @param t 时间
     * @return
     */
    private fun deCasteljauY(i: Int, j: Int, t: Float): Float {
        return if (i == 1) {
            (1 - t) * controlPointList[j].y + t * controlPointList[j + 1].y
        } else
            (1 - t) * deCasteljauY(i - 1, j, t) + t * deCasteljauY(i - 1, j + 1, t)
    }

    /**
     * 判断坐标是否在合法区域中
     *
     * @param x
     * @param y
     * @return
     */
    fun isLegalTouchRegion(x: Float, y: Float): Boolean {
        if ((x <= REGION_WIDTH) || (x >= mWidth - REGION_WIDTH) || y <= REGION_WIDTH || y >= mHeight - REGION_WIDTH) {
            return false
        }
        val rectF = Rectangle()
        for (point: Point in controlPointList) {
            if (mCurPoint != null && mCurPoint == point) { // 判断是否是当前控制点
                continue
            }
            rectF[point.x - REGION_WIDTH, point.y - REGION_WIDTH, point.x + REGION_WIDTH] = point.y + REGION_WIDTH
            if (rectF.contains(x, y)) {
                return false
            }
        }
        return true
    }

    /**
     * 获取合法控制点
     *
     * @param x
     * @param y
     * @return
     */
    fun getLegalControlPoint(x: Float, y: Float): Point? {
        val rectF = Rectangle()
        for (point: Point in controlPointList) {
            rectF[point.x - REGION_WIDTH, point.y - REGION_WIDTH, point.x + REGION_WIDTH] =
                point.y + REGION_WIDTH
            if (rectF.contains(x, y)) {
                return point
            }
        }
        return null
    }


    /**
     * 判断手指坐标是否在合法区域中
     *
     * @param x
     * @param y
     * @return
     */
    fun isLegalFingerRegion(x: Float, y: Float): Boolean {
        if (mCurPoint != null) {
            val rectF = Rectangle(
                mCurPoint!!.x - (FINGER_RECT_SIZE shr 1),
                mCurPoint!!.y - (FINGER_RECT_SIZE shr 1), mCurPoint!!.x +
                        (FINGER_RECT_SIZE shr 1), mCurPoint!!.y +
                        (FINGER_RECT_SIZE shr 1)
            )
            return rectF.contains(x, y)
        }
        return false
    }



    fun isReady(): Boolean {
        return (mState and STATE_READY) == STATE_READY
    }

    fun isRunning(): Boolean {
        return (mState and STATE_RUNNING) == STATE_RUNNING
    }

    fun isTouchable(): Boolean {
        return (mState and STATE_TOUCH) == STATE_TOUCH
    }

    fun isStop(): Boolean {
        return (mState and STATE_STOP) == STATE_STOP
    }

    /**
     * 开始
     */
    open fun start() {
        if (isReady()) {
            bezierPoint = null
            instantTangentPointList.clear()
            bezierPointList = buildBezierPoints()
            if (mTangent) {
                tangentPointList = buildTangentPoints()
            }
            mState = mState and STATE_READY.inv()
            mState = mState and STATE_TOUCH.inv()
            mState = mState or STATE_RUNNING
            //invalidate()
        }
    }

    /**
     * 停止
     */
    open fun stop() {
        if (isRunning()) {
            //mHandler.removeMessages(HANDLER_WHAT)
            mR = 0
            mState = mState and STATE_RUNNING.inv()
            mState = mState and STATE_STOP.inv()
            mState = mState or (STATE_READY or STATE_TOUCH)
            //invalidate()
        }
    }

    /**
     * 添加控制点
     */
    open fun addPoint(): Boolean {
        if (isReady()) {
            val size = controlPointList.size
            if (size >= MAX_COUNT + 1) {
                return false
            }
            val x = controlPointList[size - 1].x
            val y = controlPointList[size - 1].y
            val r = mWidth / 5
            val region = arrayOf(
                floatArrayOf(
                    0f,
                    r.toFloat()
                ), floatArrayOf(
                    0f,
                    -r.toFloat()
                ), floatArrayOf(
                    r.toFloat(),
                    r.toFloat()
                ), floatArrayOf(
                    -r.toFloat(),
                    -r.toFloat()
                ), floatArrayOf(
                    r.toFloat(),
                    0f
                ), floatArrayOf(
                    -r.toFloat(),
                    0f
                ), floatArrayOf(
                    0f,
                    1.5f * r
                ), floatArrayOf(
                    0f,
                    -1.5f * r
                ), floatArrayOf(
                    (1.5f * r),
                    1.5f * r
                ), floatArrayOf(
                    -1.5f * r,
                    -1.5f * r
                ), floatArrayOf(
                    1.5f * r,
                    0f
                ), floatArrayOf(
                    -1.5f * r,
                    0f
                ), floatArrayOf(
                    0f,
                    (2 * r).toFloat()
                ), floatArrayOf(
                    0f,
                    (-2 * r).toFloat()
                ), floatArrayOf(
                    (2 * r).toFloat(),
                    (2 * r).toFloat()
                ), floatArrayOf(
                    (-2 * r).toFloat(),
                    (-2 * r).toFloat()
                ), floatArrayOf(
                    (2 * r).toFloat(),
                    0f
                ), floatArrayOf(
                    (-2 * r).toFloat(),
                    0f
                )
            )
            var t = 0
            val len = region.size
            while (true) {  // 随机赋值
                t++
                if (t > len) {  // 超出region长度，跳出随机赋值
                    t = 0
                    break
                }
                val rand: Int = Random.nextInt(len)
                val px = x + region[rand][0]
                val py = y + region[rand][1]
                if (isLegalTouchRegion(px, py)) {
                    controlPointList.add(Point(px, py))
                    //invalidate()
                    break
                }
            }
            if (t == 0) {   // 超出region长度而未赋值时，循环赋值
                for (floats: FloatArray in region) {
                    val px = x + floats[0]
                    val py = y + floats[1]
                    if (isLegalTouchRegion(px, py)) {
                        controlPointList.add(Point(px, py))
                        //invalidate()
                        break
                    }
                }
            }
            return true
        }
        return false
    }

    /**
     * 删除控制点
     */
    open fun delPoint(): Boolean {
        if (isReady()) {
            val size = controlPointList.size
            if (size <= 2) {
                return false
            }
            controlPointList.removeAt(size - 1)
            //invalidate()
            return true
        }
        return false
    }

    /**
     * 贝塞尔曲线阶数
     *
     * @return
     */
    open fun getOrder(): Int {
        return controlPointList.size - 1
    }

    /**
     * 设置贝塞尔曲线阶数
     *
     * @param order
     */
    open fun setOrder(order: Int) {
        if (getOrder() == order) {
            return
        }
        stop()
        val size = getOrder() - order
        if (size > 0) {
            for (i in 0 until size) {
                delPoint()
            }
        } else if (size < 0) {
            for (i in -size downTo 1) {
                addPoint()
            }
        }
    }

    companion object{
        const val MAX_COUNT = 7 // 贝塞尔曲线最大阶数

        const val REGION_WIDTH = 30// 合法区域宽度

        const val FINGER_RECT_SIZE = 60 // 矩形尺寸

        const val BEZIER_WIDTH = 10 // 贝塞尔曲线线宽

        const val TANGENT_WIDTH = 6 // 切线线宽

        const val CONTROL_WIDTH = 12 // 控制点连线线宽

        const val CONTROL_RADIUS = 12 // 控制点半径

        const val TEXT_SIZE = 40 // 文字画笔尺寸

        const val TEXT_HEIGHT = 60 // 文本高度

        const val RATE = 10 // 移动速率

        const val HANDLER_WHAT = 100
        const val FRAME = 1000 // 1000帧

        val TANGENT_COLORS = arrayOf(
            "#7fff00",
            "#7a67ee",
            "#ee82ee",
            "#ffd700",
            "#1c86ee",
            "#8b8b00"
        ) // 切线颜色

        const val STATE_READY = 0x0001
        const val STATE_RUNNING = 0x0002
        const val STATE_STOP = 0x0004
        const val STATE_TOUCH = 0x0010

        const val SMOOTHNESS = 0.5f

        fun calculateControlPoint(pointList: List<Point>):List<Point> {
            val controlPointList= mutableListOf<Point>()
            if (pointList.size <= 1) {
                return emptyList()
            }
            for ((i, point) in pointList.withIndex()) {
                when (i) {
                    0 -> {//第一项
                        //添加后控制点
                        val nextPoint = pointList[i + 1]
                        val controlX = point.x + (nextPoint.x - point.x) * SMOOTHNESS
                        val controlY = point.y
                        controlPointList.add(Point(controlX, controlY))
                    }
                    pointList.size - 1 -> {//最后一项
                        //添加前控制点
                        val lastPoint = pointList[i - 1]
                        val controlX = point.x - (point.x - lastPoint.x) * SMOOTHNESS
                        val controlY = point.y
                        controlPointList.add(Point(controlX, controlY))
                    }
                    else -> {//中间项
                        val lastPoint = pointList[i - 1]
                        val nextPoint = pointList[i + 1]
                        val k = (nextPoint.y - lastPoint.y) / (nextPoint.x - lastPoint.x)
                        val b = point.y - k * point.x
                        //添加前控制点
                        val lastControlX = point.x - (point.x - lastPoint.x) * SMOOTHNESS
                        val lastControlY = k * lastControlX + b
                        controlPointList.add(Point(lastControlX, lastControlY))
                        //添加后控制点
                        val nextControlX = point.x + (nextPoint.x - point.x) * SMOOTHNESS
                        val nextControlY = k * nextControlX + b
                        controlPointList.add(Point(nextControlX, nextControlY))
                    }
                }
            }
            return controlPointList
        }

        fun buildBezierLine(startX: Float, startY: Float,
                            endX: Float, endY: Float, points: Array<Point>
        ): Path {
            val line = BezierLine(Point(startX,startY), Point(endX,endY)).apply {
                addControlPoints(startPoint)
                addControlPoints(points)
                addControlPoints(endPoint)
            }
            return line.bezierPath
        }

        fun buildBezierLineUseKnots(mControlPointList:List<Point>): Path {
            val mValuePointList = calculateControlPoint(mControlPointList)
            val mPath = Path()
            val firstPoint = mValuePointList.first()
            //mPath.moveTo(firstPoint.x, height.toFloat())
            mPath.moveTo(firstPoint.x, firstPoint.y)
            for (i in 0 until mValuePointList.size * 2 - 2 step 2) {
                val leftControlPoint = mControlPointList[i]
                val rightControlPoint = mControlPointList[i + 1]
                val rightPoint = mValuePointList[i / 2 + 1]
                mPath.cubicTo(
                    leftControlPoint.x,
                    leftControlPoint.y,
                    rightControlPoint.x,
                    rightControlPoint.y,
                    rightPoint.x,
                    rightPoint.y
                )
            }
            //val lastPoint = mValuePointList.last()
            //填充渐变色
            //mPath.lineTo(lastPoint.x, height.toFloat())
            //mPath.lineTo(firstPoint.x, height.toFloat())
            return mPath
        }

    }

}

class SampleBezierLine(startPoint: Point, val controlPoint: Point, endPoint: Point): Line(startPoint, endPoint)



